﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using HtmlAgilityPack;
using System.Net;
using System.Threading;
using System.Diagnostics;
using System.IO;
using Microsoft.Win32;

namespace Proxificator
{
    public sealed class Proxificator
    {
        private static List<Proxy> listProxy;
        public static List<Proxy> listJudgedProxy;

        private static MatchCollection matchCollectionIp;
        private static MatchCollection matchCollectionPort;
        private static string sPatternCountry =
            //@"(?<=GEOIP_COUNTRY_NAME)([ a-zA-Z\/]*?)(?=\sHTTP)";
            @"(?<=GEOIP_COUNTRY_NAME..)[ a-zA-Z\/]*";
        public static string sPattern =
            @"\b(?:(?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9][0-9]?)\:(?:6553[0-5]|655[0-2][0-9]|65[0-4][0-9][0-9]|6[0-4][0-9][0-9][0-9]|[0-5]?[0-9]?[0-9]?[0-9]?[0-9])\b";
        public static string sPatternIp = @"(^|[ \s])(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)([ \s]|$)";
        private static string regexPort =
            @"(?:6553[0-5]|655[0-2][0-9]|65[0-4][0-9][0-9]|6[0-4][0-9][0-9][0-9]|[0-5]?[0-9]?[0-9]?[0-9]?[0-9])";
        public static string sPatternPort =
            @"(^|[ \s])" + regexPort + @"([ \s]|$)";
        private static Regex regIpPort = new Regex(sPattern);
        private static Regex regIp = new Regex(sPatternIp);
        private static Regex regPort = new Regex(sPatternPort);

        private static Stack<String> ip = new Stack<String>();

        private static WebProxy webProxy = null;
        private static string sourceJudge = null;
        private static int timeOut = 20000;
        private static int maxThread = -1;
        private static ThreadList[] listThread;
        private static Dictionary<int, int> mapThreadId;
        
        public static int threadAlive = 0;
        public static int proxyChecked;
        public static int totalProxy;
        private static int vElite;
        private static int vAnonymous;
        private static int vTransparent;
        private static int vBadData;
        private static int vBadProxy;

        public static int elite
        {
            get
            {
                return vElite;
            }
        }
        public static int anonymous
        {
            get
            {
                return vAnonymous;
            }
        }
        public static int transparent
        {
            get
            {
                return vTransparent;
            }
        }
        public static int badData
        {
            get
            {
                return vBadData;
            }
        }
        public static int badProxy
        {
            get
            {
                return vBadProxy;
            }
        }
        
        public static string ipUser = null;

        private static string[] cekHTTP = {
                                              "HTTP_VIA",
                                              "HTTP_PROXY_CONNECTION",
                                              "HTTP_X_FORWARDED_FOR",
                                              "HTTP_FORWARDED",
                                              "HTTP_CLIENT_IP",
                                              "HTTP_XROXY_CONNECTION",
                                              "HTTP_MAX_FORWARDS",
                                              "HTTP_X_BLUECOAT_VIA"
                                          };

        private static void getIpUser()
        {
            HtmlDocument htmldoc = null;
            HtmlWeb htmlweb = new HtmlWeb();

            htmlweb.PreRequest = delegate(HttpWebRequest webRequest)
            {
                webRequest.Proxy = null;
                webRequest.KeepAlive = false;
                return true;
            };

            try
            {
            	htmldoc = htmlweb.Load("http://automation.whatismyip.com/n09230945.asp");
            }
            catch (System.Exception ex)
            {
                throw (new Exception());
            }
            ipUser = htmldoc.DocumentNode.InnerText.Trim();
        }

        private Proxificator()
        {
            
        }

        public WebProxy UseProxy
        {
            set
            {
                webProxy = value;
            }
        }

        public static string vSourceJudge
        {
            get
            {
                return sourceJudge;
            }
            set
            {
                sourceJudge = value;
            }
        }

        public static int vTimeOut
        {
            get
            {
                return timeOut;
            }
            set
            {
                timeOut = value;
            }
        }

        public static int vMaxThread
        {
            get
            {
                return maxThread;
            }
            set
            {
                maxThread = value;
            }
        }

        private static void traverse(HtmlNode cur)
        {
            if (cur == null) return;
            
            if (cur.OriginalName == "#text")
            {
                matchCollectionIp = regIpPort.Matches(WebUtility.HtmlDecode(cur.InnerText));
                for (int i = 0; i < matchCollectionIp.Count; i++)
                {
                    listProxy.Add(new Proxy(matchCollectionIp[i].Value, 0, "Unknown"));
                }
                if (matchCollectionIp.Count == 0)
                {
                    matchCollectionIp = regIp.Matches(cur.InnerText);
                    if (matchCollectionIp.Count > 0)
                    {
                        for (int i = 0; i < matchCollectionIp.Count; i++)
                        {
                            if (matchCollectionIp[i].Value.Trim() != ipUser)
                            {
                                ip.Push(matchCollectionIp[i].Value);
                                //System.IO.File.AppendAllText(@"D:\PBK\WriteText.txt", matchCollectionIp[i].Value.Trim() + "\n");
                            }
                        }
                        //System.IO.File.AppendAllText(@"D:\PBK\WriteText.txt", ip + "\n");
                    }
                    if (ip.Count > 0)
                    {
                        matchCollectionPort = regPort.Matches(cur.InnerText);
                        int len = Math.Min(matchCollectionPort.Count, ip.Count);
                        for (int i = len - 1; i >= 0; i--)
                        {
                            listProxy.Add(new Proxy((ip.Pop()).Trim() + ":" + matchCollectionPort[i].Value.Trim(), 0, "Unknown"));
                        }
                        if (matchCollectionPort.Count > 0)
                        {
                            ip.Clear();
                        }
                    }
                }
            }

            traverse(cur.FirstChild);
            traverse(cur.NextSibling);
        }

        public static List<Proxy> extractProxy(string sourceList)
        {
            HtmlDocument htmldoc = null;
            HtmlWeb htmlweb = new HtmlWeb();
            HtmlNode htmlnode = null;

            listProxy = new List<Proxy>();

            htmlweb.PreRequest = delegate(HttpWebRequest webRequest)
            {
                webRequest.KeepAlive = false;
                webRequest.Proxy = null;
                return true;
            };

            try
            {
            	getIpUser();
            }
            catch (System.Exception ex)
            {
                return null;
            }
            if (webProxy == null)
            {
                try
                {
                    htmldoc = htmlweb.Load(sourceList);
                }
                catch (WebException ex)
                {
                    return null;
                }
            }
            else
            {
                try
                {
                    htmldoc = htmlweb.Load(sourceList, "GET", webProxy, null);
                }
                catch (WebException ex)
                {
                    return null;
                }
            }

            htmlnode = htmldoc.DocumentNode.SelectSingleNode("//body");
            if (htmlnode == null)
            {
                return null;
            }
            else
            {
                traverse(htmlnode.FirstChild);
                ip.Clear();

                removeDuplicate(ref listProxy);
                return listProxy;
            }
        }

        private static void preJudge()
        {
            listThread = new ThreadList[maxThread];
            threadAlive = 0;
            proxyChecked = maxThread - 1;
            mapThreadId = new Dictionary<int, int>();
            vElite = vAnonymous = vTransparent = vBadProxy = vBadData = 0;
        }

        public static int Judge(ref List<Proxy> setProxy)
        {
            try
            {
            	getIpUser();
            }
            catch (System.Exception ex)
            {
                return 0;
            }

            if (String.IsNullOrEmpty(sourceJudge))
            {
                sourceJudge = @"http://zaraki-kenpachi.com/test.cgi";
            }
            if (timeOut == -1)
            {
                timeOut = 20000;
            }
            if (maxThread == -1)
            {
                maxThread = (setProxy.Count < 50 ? setProxy.Count : 50);
            }
            else
                if (maxThread > setProxy.Count)
                {
                    maxThread = setProxy.Count;
                }

            preJudge();

            listJudgedProxy = setProxy;
            totalProxy = setProxy.Count;

            if (setProxy == null)
            {
                return 0;
            }

            for (int i = 0; i < maxThread; i++)
            {
                Proxy tempPr = listJudgedProxy[i];
                try
                {
                    listThread[i] = new ThreadList();
                    listThread[i].thread = new Thread(() => 
                        {
                            //int indeks = i;
                            //Stopwatch sw = Stopwatch.StartNew();
                            doJudge(ref tempPr);
                            //double perf = sw.Elapsed.TotalMilliseconds;
                            //System.IO.File.AppendAllText(@"D:\PBK\WriteText" + indeks + ".txt", indeks + " " + perf + "\n");
                        });
                    listThread[i].thread.IsBackground = true;
                    listThread[i].isAlive = true;
                    listThread[i].thread.Priority = ThreadPriority.BelowNormal;
                    mapThreadId.Add(listThread[i].thread.ManagedThreadId, i);
                    listThread[i].thread.Start();
                    Interlocked.Increment(ref threadAlive);
                    Thread.Sleep(100);
                }
                catch (System.Exception ex)
                {
                    Interlocked.Decrement(ref threadAlive);
                }
            }
            return 1;
        }

        public static bool setProxy(Proxy pr)
        {
            RegistryKey registry = null;
            bool success = false;
            try
            {
                using (registry = Registry.CurrentUser.OpenSubKey("Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings", true))
                {
                    registry.SetValue("ProxyEnable", 1);
                    registry.SetValue("ProxyServer", pr.vIpPort);
                    registry.SetValue("ProxyOverride", "local;<local>");
                }
                success = true;
            }
            catch (System.Exception ex)
            {
                throw;
            }
            finally
            {
                if (registry != null)
                {
                    registry.Dispose();
                }
            }

            return success;
        }

        public static void waitAllThreadTerminated()
        {
            bool notyetexit = true;
            Stopwatch sw = Stopwatch.StartNew();
            while (notyetexit)
            {
                if (sw.Elapsed.TotalMinutes > 1.5)
                {
                    killAllThread();
                    sw.Stop();
                    sw = null;
                    return;
                }
                notyetexit = false;
                for (int i = 0; i < maxThread; i++)
                {
                    if (listThread[i].thread.IsAlive)
                    {
                        notyetexit = true; break;
                    }
                }
            }
            sw.Stop();
            sw = null;
        }

        public static void killAllThread()
        {
            for (int i = 0; i < maxThread; i++)
            {
                if (listThread[i].thread.IsAlive)
                {
                    listThread[i].thread.Abort();
                    Thread.Sleep(100);
                }
            }
        }

        public static void removeDuplicate(ref List<Proxy> arrayProxy)
        {
            HashSet<Proxy> setProxy = new HashSet<Proxy>(arrayProxy);
            List<Proxy> resProxy = new List<Proxy>(setProxy);
            arrayProxy = resProxy;
        }

        public static void stopJudge()
        {
            for (int i = 0; i < maxThread; i++)
            {
                if (listThread[i].thread.IsAlive)
                    listThread[i].isAlive = false;
            }
        }

        private static void doJudge(ref Proxy proxy)
        {
            Proxy pr = proxy;
            bool noException = true;
            bool noException2 = true;
            bool adaHttp = false;
            bool adaIpUser = false;
            string cek = null;
            Match mc = null;
            //HtmlAgilityPack.HtmlWeb htmlweb = new HtmlAgilityPack.HtmlWeb();
            //HtmlAgilityPack.HtmlDocument htmldoc = null;

            HttpWebRequest webRequest = null;
            HttpWebResponse webResponse = null;
            Stopwatch sw = new Stopwatch();

            /*htmlweb.PreRequest = delegate(HttpWebRequest webRequest1)
            {
                webRequest1.Timeout = timeOut;
                webRequest1.KeepAlive = false;
                webRequest1.Method = "GET";
                //webRequest.ServicePoint.ConnectionLimit = maxThread;
                return true;
            };*/

            while (true)
            {
                if (listThread[mapThreadId[Thread.CurrentThread.ManagedThreadId]].isAlive)
                {
                    if (pr.vStatus == -2 || (!(Regex.IsMatch(pr.vIpPort, sPattern))))
                    {
                        pr.vStatus = -2;
                        Interlocked.Increment(ref vBadData);
                    }
                    else
                    {
                        noException = true;
                        //htmldoc = null;
                        webRequest = (HttpWebRequest)WebRequest.Create(sourceJudge);
                        webRequest.Proxy = new WebProxy(pr.vIp, pr.vPort);
                        webRequest.Timeout = timeOut;
                        webRequest.ReadWriteTimeout = 180000;
                        webRequest.ProtocolVersion = HttpVersion.Version10;
                        webRequest.KeepAlive = false;
                        webRequest.ServicePoint.Expect100Continue = false;
                        webRequest.Method = "GET";
                        webRequest.Accept = "text/html";
                        webRequest.UserAgent = @"Mozilla/5.0 (Windows NT 6.2; WOW64; rv:17.0) Gecko/17.0 Firefox/17.0";
                        webRequest.ContentType = "text/plain";

                        try
                        {
                            sw.Restart();
                            webResponse = (HttpWebResponse)webRequest.GetResponse();
                            sw.Stop();
                        }
                        catch (WebException ex)
                        {
                            pr.vStatus = -1;
                            Interlocked.Increment(ref vBadProxy);
                            noException = false;
                        }
                        catch (Exception ex)
                        {
                            pr.vStatus = -1;
                            Interlocked.Increment(ref vBadProxy);
                            noException = false;
                        }

                        if (noException)
                        {
	                        if (webResponse == null || webResponse.StatusCode != HttpStatusCode.OK)
	                        {
	                            pr.vStatus = -1;
                                Interlocked.Increment(ref vBadProxy);
	                        }
	                        else
	                        {
	                            pr.vSpeed = (int)sw.ElapsedMilliseconds;
                                noException2 = true;
                                try
                                {
                                    using (Stream stream = webResponse.GetResponseStream())
                                    {
                                        using (BufferedStream bs = new BufferedStream(stream))
                                        {
                                            using (StreamReader sr = new StreamReader(bs))
                                            {
                                                //try
                                                //{
                                                cek = sr.ReadToEnd();
                                                //}
                                                //catch (Exception ex)
                                                //{
                                                //}
                                            }
                                        }
                                    }
                                }
                                catch (Exception ex)
                                {
                                    pr.vStatus = -1;
                                    Interlocked.Increment(ref vBadProxy);
                                    noException2 = false;
                                }

                                if (webResponse != null) webResponse.Close();
                                webRequest = null;

                                if (noException2)
                                {
                                    //cek = sr.ReadToEnd();
                                    if (String.IsNullOrEmpty(cek))
                                    {
                                        pr.vStatus = -1;
                                        Interlocked.Increment(ref vBadProxy);
                                    }
                                    else
                                    {
                                        adaHttp = false;
                                        adaIpUser = false;

                                        for (int i = 0; i < cekHTTP.Length; i++)
                                        {
                                            if (cek.Contains(cekHTTP[i]))
                                            {
                                                adaHttp = true;
                                                break;
                                            }
                                        }
                                        if (cek.Contains(ipUser))
                                        {
                                            adaIpUser = true;
                                        }

                                        if (adaIpUser && !adaHttp)
                                        {
                                            pr.vStatus = -1;
                                            Interlocked.Increment(ref vBadProxy);
                                        }
                                        else
                                        {
                                            mc = Regex.Match(cek, sPatternCountry);
                                            if (mc.Success)
                                            {
                                                pr.vCountry = mc.Value;
                                            }
                                            else
                                            {
                                                pr.vCountry = "N/A";
                                            }

                                            if (adaIpUser)
                                            {
                                                pr.vStatus = 3;
                                                Interlocked.Increment(ref vTransparent);
                                            }
                                            else
                                            {
                                                if (adaHttp)
                                                {
                                                    pr.vStatus = 2;
                                                    Interlocked.Increment(ref vAnonymous);
                                                }
                                                else
                                                {
                                                    pr.vStatus = 1;
                                                    Interlocked.Increment(ref vElite);
                                                }
                                            }
                                        }
                                    } 
                                }//
	                        }
                        }
                    }

                    //checkForNext
                    if (listThread[mapThreadId[Thread.CurrentThread.ManagedThreadId]].isAlive)
                    {
                        if (proxyChecked < totalProxy)
                        {
                            int i = Interlocked.Increment(ref proxyChecked);
                            if (i >= totalProxy)
                            {
                                //if (threadAlive <= 10)
                                //{
                                //Thread.Sleep(100);
                                //}
                                Interlocked.Decrement(ref threadAlive);
                                //if (sr != null) sr.Close();
                                return;
                                //Thread.CurrentThread.Abort();
                            }
                            else
                            {
                                pr = listJudgedProxy[i];
                                //if (sr != null) sr.Close();
                                continue;
                            }
                        }
                        else
                        {
                            //if (threadAlive <= 10)
                            //{
                            //Thread.Sleep(1000);
                            //}
                            Interlocked.Decrement(ref threadAlive);
                            //if (sr != null) sr.Close();
                            return;
                            //Thread.CurrentThread.Abort();
                        }
                    }
                    else
                    {
                        //if (threadAlive <= 10)
                        //{
                        //Thread.Sleep(1000);
                        //}
                        Interlocked.Decrement(ref threadAlive);
                        //if (sr != null) sr.Close();
                        return;
                        //Thread.CurrentThread.Abort();
                    }
                }
                else
                {
                    //if (threadAlive <= 10)
                    //{
                    //Thread.Sleep(1000);
                    //}
                    Interlocked.Decrement(ref threadAlive);
                    //if (sr != null) sr.Close();
                    return;
                    //Thread.CurrentThread.Abort();
                }
            }
        }

    }
}
